package dao

import (
	"gitee.com/mangenotwork/game-douniu/api/entity"
	"github.com/mangenotwork/common/log"
	"github.com/mangenotwork/common/utils"
	"sort"
	"strings"
)

/*
斗牛的基本规则：

牌的大小：除了大小王（A和K）以外，其他牌都按照其点数大小排列，A为最低点数，而K为最高点数。 JQK都为10

牌型比较：玩家需要将自己的手牌分成两组，一组是三张牌，另一组是两张牌。三张牌中的任意一张与其他两张牌组合成的牌型需要进行比较，决定胜负。

牌型分类：
无牛：三张牌中没有组合成10的倍数。
有牛：三张牌中有组合成10的倍数的牌型。剩下两张牌和余10后的点数就是几牛
牛牛：三张牌中有组合成10的倍数的牌型，并且剩余的两张牌中也恰好是10的倍数。
五小，5张牌牌点总数小于或者等于10。
炸弹：四张牌点一样的牌。
金牛：五张十以上的花牌组成的“斗牛”。
银牛：十和十以上的花牌组成的“斗牛”。


比牌  先比牌型，牌型一样比单牌大小， 牌一样比花色， 完全一样庄家胜
各牌型间的大小 :  炸弹>五小>金牛>银牛>牛牛>有牛>没牛。
单牌的大小为 : K>Q>J>10>9>8>7>6>5>4>3>2>A。
花色大小 :  黑桃>红桃>梅花>方块。


赔率倍数
炸弹 :3倍
五小:3倍
金牛:3倍
银牛:3倍
牛牛:3倍
有牛：7,8,9点2倍，其他点数1倍
没牛:  1倍
（都没牛的情况，庄家和闲家最大点数都是10，那么庄家赢）
如果闲家输了 3倍， 闲家分：+ 下注*3，闲家: -下注*3
庄家特权：没牛的情况,庄家和闲家最大点数小于等于10，那么庄家赢，


*/

type RuleDao struct {
}

// 获取牌型
func (dao *RuleDao) GetDouNiuResult(poker []*entity.PokerData) *entity.DouNiuResult {

	pokerStr := []string{poker[0].PokerStr, poker[1].PokerStr, poker[2].PokerStr, poker[3].PokerStr, poker[4].PokerStr}

	result := &entity.DouNiuResult{
		Poker:       poker,
		NiuPoker:    make([]*entity.PokerData, 0),
		NiuNiuPoker: make([]*entity.PokerData, 0),
	}

	// 特殊牌型的判断
	// 0:无牛  1:有牛  2:牛牛  3:五小  4:银牛  5:金牛  6:炸弹
	niu, zd := special(poker)
	if niu != 0 { // 特殊牌型直接返回
		result.HasNiu = int64(niu)
		if result.HasNiu == 3 {
			result.HasNiuStr = "五小 | " + strings.Join(pokerStr, "")
		}
		if result.HasNiu == 4 {
			result.HasNiuStr = "银牛 | " + strings.Join(pokerStr, "")
		}
		if result.HasNiu == 5 {
			result.HasNiuStr = "金牛 | " + strings.Join(pokerStr, "")
		}
		if result.HasNiu == 6 {
			result.HasNiuStr = "炸弹 | " + strings.Join(pokerStr, "")
			result.SpecialZD = zd
		}
		return result
	}

	// 将牌分成不重复的三个数的组合
	nums := make([][]*entity.PokerData, 0)
	for i := 0; i < len(poker)-2; i++ {
		for j := i + 1; j < len(poker)-1; j++ {
			for k := j + 1; k < len(poker); k++ {
				nums = append(nums, []*entity.PokerData{poker[i], poker[j], poker[k]})
			}
		}
	}

	// 判断有无10的倍数
	for _, v := range nums {
		sum := v[0].Points + v[1].Points + v[2].Points

		if sum%10 == 0 { // 有牛
			result.NiuPoker = append(result.NiuPoker, v...)

			// 牛牛：三张牌中有组合成10的倍数的牌型，并且剩余的两张牌中也恰好是10的倍数。
			nn, _ := sliceDifference(poker, v)
			nnSum := nn[0].Points + nn[1].Points
			if nnSum%10 == 0 { // 牛牛
				niu = 2
				result.NiuNiuPoker = append(result.NiuNiuPoker, nn...)
				break
			}

			niu = 1
			break
		}

	}

	result.HasNiu = int64(niu)

	if result.HasNiu == 0 {
		result.HasNiuStr = "无牛 | " + strings.Join(pokerStr, "")
	}
	if result.HasNiu == 1 {
		niuPokerStr := []string{result.NiuPoker[0].PokerStr, result.NiuPoker[1].PokerStr, result.NiuPoker[2].PokerStr}
		nn, _ := sliceDifference(poker, result.NiuPoker)
		score := nn[0].Points + nn[1].Points
		nnStr := []string{nn[0].PokerStr, nn[1].PokerStr}
		niuNum := score % 10
		result.NiuNum = int64(niuNum)
		result.HasNiuStr = "有牛 | " + strings.Join(niuPokerStr, "") + " | " + strings.Join(nnStr, "") + " 牛" + utils.AnyToString(niuNum)
	}
	if result.HasNiu == 2 {
		niuPokerStr := []string{result.NiuPoker[0].PokerStr, result.NiuPoker[1].PokerStr, result.NiuPoker[2].PokerStr}
		niuNiuPokerStr := []string{result.NiuNiuPoker[0].PokerStr, result.NiuNiuPoker[1].PokerStr}
		result.HasNiuStr = "牛牛 | " + strings.Join(niuPokerStr, "") + " | " + strings.Join(niuNiuPokerStr, "")
	}
	return result
}

// 比牌
// master 为庄家   slave 为闲家
// true 庄家胜     false 庄家输
func (dao *RuleDao) DouNiuContrast(master, slave *entity.DouNiuResult) bool {
	// 牌型比较
	// 炸弹>五小>金牛>银牛>斗牛>牛牛>有分>没分
	if master.HasNiu > slave.HasNiu {
		// 庄胜
		return true
	} else if master.HasNiu == slave.HasNiu {
		// 牌型一样 单牌比较
		// K>Q>J>10>9>8>7>6>5>4>3>2>A

		// 牌按点数降序
		sort.Slice(master.Poker, func(i, j int) bool {
			return master.Poker[i].PokerNumber > master.Poker[j].PokerNumber
		})
		sort.Slice(slave.Poker, func(i, j int) bool {
			return slave.Poker[i].PokerNumber > slave.Poker[j].PokerNumber
		})

		for _, v := range master.Poker {
			log.Debug("master ", v)
		}
		for _, v := range slave.Poker {
			log.Debug("slave ", v)
		}

		switch master.HasNiu {
		case 0: // 无牛
			return contrastNone(master, slave)

		case 1: // 有牛
			if master.NiuNum > slave.NiuNum {
				return true
			} else if master.NiuNum < slave.NiuNum {
				return false
			}
			// 一样就比牌面大小
			return contrastNone(master, slave)

		case 2, 3, 4, 5: // 牛牛,五小, 银牛, 金牛 都是就比牌面大小
			return contrastNone(master, slave)

		case 6: // 炸弹
			return contrastZD(master, slave)
		}

	}

	// 庄输
	return false
}

func sliceDifference(slice1 []*entity.PokerData, slice2 []*entity.PokerData) ([]*entity.PokerData, error) {
	var diffSlice []*entity.PokerData
	for _, value := range slice1 {
		found := false
		for _, v := range slice2 {
			if value == v {
				found = true
				break
			}
		}
		if !found {
			diffSlice = append(diffSlice, value)
		}
	}
	return diffSlice, nil
}

func special(poker []*entity.PokerData) (int, string) {
	// 0:无牛  1:有牛  2:牛牛  3:五小  4:银牛  5:金牛  6:炸弹

	// 判断 五小
	sum := 0
	for _, p := range poker {
		sum += p.Points
	}
	if sum <= 10 {
		return 3, ""
	}

	// 判断 炸弹
	pMapCount := make(map[string]int)
	for _, p := range poker {
		if _, ok := pMapCount[p.Poker]; ok {
			pMapCount[p.Poker] += 1
		} else {
			pMapCount[p.Poker] = 1
		}
	}
	for k, v := range pMapCount {
		if v == 4 {
			return 6, k
		}
	}

	// 判断 银牛 金牛
	isMax10 := true
	is10 := false
	for _, p := range poker {
		if p.Points < 10 {
			isMax10 = false
		}
		if p.Poker == "10" {
			is10 = true
		}
	}
	if isMax10 {
		if is10 { // 银牛
			return 4, ""
		} else { // 金牛
			return 5, ""
		}
	}

	return 0, ""
}

func notZdPoints(poker []*entity.PokerData, pointsStr string) *entity.PokerData {
	for _, v := range poker {
		if v.Poker != pointsStr {
			return v
		}
	}
	return nil
}

// 都是炸弹比较
func contrastZD(master, slave *entity.DouNiuResult) bool {
	// 炸弹点数比较
	if master.SpecialZD > slave.SpecialZD {
		// 庄家的炸弹大
		return true
	} else if master.SpecialZD == slave.SpecialZD {
		// 炸弹一样比点数
		masterP := notZdPoints(master.Poker, master.SpecialZD)
		if masterP == nil {
			log.Error("牌型有异常请检查", master.Poker)
			return false
		}
		slaveP := notZdPoints(slave.Poker, slave.SpecialZD)
		if slaveP == nil {
			log.Error("牌型有异常请检查", slave.Poker)
			return true
		}
		if masterP.Poker > slaveP.Poker {
			return true
		} else if masterP.Poker == slaveP.Poker {
			// 比花色 // 黑桃>红桃>梅花>方块
			if masterP.HuaSe >= slaveP.HuaSe {
				return true
			}
			return false
		}
	}
	return false
}

// 都是无牛比较
// 前置: 牌需要按牌面倒叙
func contrastNone(master, slave *entity.DouNiuResult) bool {
	for i := 0; i < 5; i++ {
		if master.Poker[i].PokerNumber > slave.Poker[i].PokerNumber {
			return true
		} else if master.Poker[i].PokerNumber < slave.Poker[i].PokerNumber {
			return false
		}
	}
	// 跑到这个牌点数是一样的,就需要比较花色
	for i := 0; i < 5; i++ {
		if master.Poker[i].HuaSe > slave.Poker[i].HuaSe {
			return true
		} else if master.Poker[i].HuaSe < slave.Poker[i].HuaSe {
			return false
		}
	}
	return true
}

// 变牌  传入一幅牌将牌进行变为想要的牌型
// 1炸弹 2五小 3金牛 4银牛 5牛牛 6有牛 7没牛
// 61牛1 62牛2 63牛3
func (dao *RuleDao) ChangeDouNiuPoker(poker []*entity.PokerData, niuType int64) ([]*entity.PokerData, bool) {

	// 将牌4张组合
	poker4Map := make(map[string][]*entity.PokerData)
	for _, v := range poker {
		if _, ok := poker4Map[v.Poker]; !ok {
			poker4Map[v.Poker] = make([]*entity.PokerData, 0)
		}
		poker4Map[v.Poker] = append(poker4Map[v.Poker], v)
	}

	jqk := make([]*entity.PokerData, 0)
	jqk = append(jqk, poker4Map[entity.PokerJ]...)
	jqk = append(jqk, poker4Map[entity.PokerQ]...)
	jqk = append(jqk, poker4Map[entity.PokerK]...)

	rse := make([]*entity.PokerData, 0)

	switch niuType {
	case 1: // 炸弹
		isP := ""
		for p, pList := range poker4Map {
			if len(pList) == 4 {
				rse = append(rse, pList...)
				isP = p
				break
			}
		}
		for _, v := range poker {
			if isP != v.Poker {
				rse = append(rse, v)
				break
			}
		}

	case 2: // 五小
		if len(poker4Map[entity.Poker2]) == 1 { // 一张2 至少大于等于两张A 剩下的牌拿三
			if len(poker4Map[entity.PokerA]) >= 2 && len(poker4Map[entity.Poker3]) >= 2 {
				rse = append(rse, poker4Map[entity.Poker2]...)
				rse = append(rse, poker4Map[entity.PokerA][0:2]...)
				rse = append(rse, poker4Map[entity.Poker3][0:2]...)
			} else if len(poker4Map[entity.PokerA]) >= 3 && len(poker4Map[entity.Poker3]) >= 1 {
				rse = append(rse, poker4Map[entity.Poker2]...)
				rse = append(rse, poker4Map[entity.PokerA][0:3]...)
				rse = append(rse, poker4Map[entity.Poker3][0:1]...)
			}

		} else if len(poker4Map[entity.Poker2]) == 2 { // 两张2 至少大于等于两张A 剩下的牌拿三
			if len(poker4Map[entity.PokerA]) >= 2 && len(poker4Map[entity.Poker3]) >= 1 {
				rse = append(rse, poker4Map[entity.Poker2]...)
				rse = append(rse, poker4Map[entity.PokerA][0:2]...)
				rse = append(rse, poker4Map[entity.Poker3][0:1]...)
			} else if len(poker4Map[entity.PokerA]) >= 3 {
				rse = append(rse, poker4Map[entity.Poker2]...)
				rse = append(rse, poker4Map[entity.PokerA][0:3]...)
			}

		} else if len(poker4Map[entity.Poker2]) == 3 { // 三张2 至少大于等于一张A 剩下的牌拿三
			if len(poker4Map[entity.PokerA]) >= 1 && len(poker4Map[entity.Poker3]) >= 1 {
				rse = append(rse, poker4Map[entity.Poker2]...)
				rse = append(rse, poker4Map[entity.PokerA][0:1]...)
				rse = append(rse, poker4Map[entity.Poker3][0:1]...)
			} else if len(poker4Map[entity.PokerA]) >= 2 {
				rse = append(rse, poker4Map[entity.Poker2]...)
				rse = append(rse, poker4Map[entity.PokerA][0:2]...)
			}
		}

	case 3: // 金牛
		if len(jqk) >= 5 {
			jqk = entity.ShuffleSlice(jqk)
			rse = append(rse, jqk[0:5]...)
		}

	case 4: // 银牛
		if len(poker4Map[entity.Poker10]) == 1 && len(jqk) >= 4 {
			rse = append(rse, poker4Map[entity.Poker10][0])
			rse = append(rse, jqk[0:4]...)
		} else if len(poker4Map[entity.Poker10]) == 2 && len(jqk) >= 3 {
			rse = append(rse, poker4Map[entity.Poker10][0:2]...)
			rse = append(rse, jqk[0:3]...)
		} else if len(poker4Map[entity.Poker10]) == 3 && len(jqk) >= 2 {
			rse = append(rse, poker4Map[entity.Poker10][0:3]...)
			rse = append(rse, jqk[0:2]...)
		}

	case 5: // 牛牛
		for i := 0; i < len(poker); i++ {
			pi := poker[i]
			for j := 0; j < len(poker); j++ {
				if i == j {
					continue
				}
				pj := poker[j]
				for z := 0; z < len(poker); z++ {
					if z == i || z == j {
						continue
					}
					pz := poker[z]
					if (pi.Points+pj.Points+pz.Points)%10 == 0 {
						rse = append(rse, []*entity.PokerData{pi, pj, pz}...)
						goto Next
					}
				}
			}
		}
	Next:
		for _, v := range rse {
			poker = entity.TakeOnePoker(poker, v)
		}
		for i := 0; i < len(poker); i++ {
			pi := poker[i]
			for j := 0; j < len(poker); j++ {
				pj := poker[j]
				if i == j {
					continue
				}
				if (pi.Points+pj.Points)%10 == 0 {
					rse = append(rse, []*entity.PokerData{pi, pj}...)
					goto End
				}
			}
		}

	case 7: // 没牛

	default:

	}

	// 有牛
	if niuType > 60 {
		for i := 0; i < len(poker); i++ {
			pi := poker[i]
			for j := 0; j < len(poker); j++ {
				if i == j {
					continue
				}
				pj := poker[j]
				for z := 0; z < len(poker); z++ {
					if z == i || z == j {
						continue
					}
					pz := poker[z]
					if (pi.Points+pj.Points+pz.Points)%10 == 0 {
						rse = append(rse, []*entity.PokerData{pi, pj, pz}...)
						goto NextNiu
					}
				}
			}
		}
	NextNiu:
		for _, v := range rse {
			poker = entity.TakeOnePoker(poker, v)
		}
		for i := 0; i < len(poker); i++ {
			pi := poker[i]
			for j := 0; j < len(poker); j++ {
				pj := poker[j]
				if i == j {
					continue
				}
				if (pi.Points+pj.Points)%10 == int(niuType-60) {
					rse = append(rse, []*entity.PokerData{pi, pj}...)
					goto End
				}
			}
		}
	}

End:

	has := false
	if len(rse) == 5 {
		has = true
		rse = entity.ShuffleSlice(rse)
	}

	return rse, has
}
